home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkFont.c < prev    next >
C/C++ Source or Header  |  1995-06-04  |  28KB  |  969 lines

  1. /* 
  2.  * tkFont.c --
  3.  *
  4.  *    This file maintains a database of looked-up fonts for the Tk
  5.  *    toolkit, in order to avoid round-trips to the server to map
  6.  *    font names to XFontStructs.  It also provides several utility
  7.  *    procedures for measuring and displaying text.
  8.  *
  9.  * Copyright (c) 1990-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  */
  15.  
  16. static char sccsid[] = "@(#) tkFont.c 1.36 95/06/04 15:27:43";
  17.  
  18. #include "tkPort.h"
  19. #include "tkInt.h"
  20.  
  21. /*
  22.  * This module caches extra information about fonts in addition to
  23.  * what X already provides.  The extra information is used by the
  24.  * TkMeasureChars procedure, and consists of two parts:  a type and
  25.  * a width.  The type is one of the following:
  26.  *
  27.  * NORMAL:        Standard character.
  28.  * TAB:            Tab character:  output enough space to
  29.  *            get to next tab stop.
  30.  * NEWLINE:        Newline character:  don't output anything more
  31.  *            on this line (character has infinite width).
  32.  * REPLACE:        This character doesn't print:  instead of
  33.  *            displaying character, display a replacement
  34.  *            sequence like "\n" (for those characters where
  35.  *            ANSI C defines such a sequence) or a sequence
  36.  *            of the form "\xdd" where dd is the hex equivalent
  37.  *            of the character.
  38.  * SKIP:        Don't display anything for this character.  This
  39.  *            is only used where the font doesn't contain
  40.  *            all the characters needed to generate
  41.  *            replacement sequences.
  42.  * The width gives the total width of the displayed character or
  43.  * sequence:  for replacement sequences, it gives the width of the
  44.  * sequence.
  45.  */
  46.  
  47. #define NORMAL        1
  48. #define TAB        2
  49. #define NEWLINE        3
  50. #define REPLACE        4
  51. #define SKIP        5
  52.  
  53. /*
  54.  * One of the following data structures exists for each font that is
  55.  * currently active.  The structure is indexed with two hash tables,
  56.  * one based on font name and one based on XFontStruct address.
  57.  */
  58.  
  59. typedef struct {
  60.     XFontStruct *fontStructPtr;    /* X information about font. */
  61.     Display *display;        /* Display to which font belongs. */
  62.     int refCount;        /* Number of active uses of this font. */
  63.     char *types;        /* Malloc'ed array giving types of all
  64.                  * chars in the font (may be NULL). */
  65.     unsigned char *widths;    /* Malloc'ed array giving widths of all
  66.                  * chars in the font (may be NULL). */
  67.     int tabWidth;        /* Width of tabs in this font. */
  68.     Tcl_HashEntry *nameHashPtr;    /* Entry in name-based hash table (needed
  69.                  * when deleting this structure). */
  70. } TkFont;
  71.  
  72. /*
  73.  * Hash table for name -> TkFont mapping, and key structure used to
  74.  * index into that table:
  75.  */
  76.  
  77. static Tcl_HashTable nameTable;
  78. typedef struct {
  79.     Tk_Uid name;        /* Name of font. */
  80.     Display *display;        /* Display for which font is valid. */
  81. } NameKey;
  82.  
  83. /*
  84.  * Hash table for font struct -> TkFont mapping. This table is
  85.  * indexed by the XFontStruct address.
  86.  */
  87.  
  88. static Tcl_HashTable fontTable;
  89.  
  90. static int initialized = 0;    /* 0 means static structures haven't been
  91.                  * initialized yet. */
  92.  
  93. /*
  94.  * To speed up TkMeasureChars, the variables below keep the last
  95.  * mapping from (XFontStruct *) to (TkFont *).
  96.  */
  97.  
  98. static TkFont *lastFontPtr = NULL;
  99. static XFontStruct *lastFontStructPtr = NULL;
  100.  
  101. /*
  102.  * Characters used when displaying control sequences.
  103.  */
  104.  
  105. static char hexChars[] = "0123456789abcdefxtnvr\\";
  106.  
  107. /*
  108.  * The following table maps some control characters to sequences
  109.  * like '\n' rather than '\x10'.  A zero entry in the table means
  110.  * no such mapping exists, and the table only maps characters
  111.  * less than 0x10.
  112.  */
  113.  
  114. static char mapChars[] = {
  115.     0, 0, 0, 0, 0, 0, 0,
  116.     'a', 'b', 't', 'n', 'v', 'f', 'r',
  117.     0
  118. };
  119.  
  120. /*
  121.  * Forward declarations for procedures defined in this file:
  122.  */
  123.  
  124. static void        FontInit _ANSI_ARGS_((void));
  125. static void        SetFontMetrics _ANSI_ARGS_((TkFont *fontPtr));
  126.  
  127. /*
  128.  *----------------------------------------------------------------------
  129.  *
  130.  * Tk_GetFontStruct --
  131.  *
  132.  *    Given a string name for a font, map the name to an XFontStruct
  133.  *    describing the font.
  134.  *
  135.  * Results:
  136.  *    The return value is normally a pointer to the font description
  137.  *    for the desired font.  If an error occurs in mapping the string
  138.  *    to a font, then an error message will be left in interp->result
  139.  *    and NULL will be returned.
  140.  *
  141.  * Side effects:
  142.  *    The font is added to an internal database with a reference count.
  143.  *    For each call to this procedure, there should eventually be a call
  144.  *    to Tk_FreeFontStruct, so that the database is cleaned up when fonts
  145.  *    aren't in use anymore.
  146.  *
  147.  *----------------------------------------------------------------------
  148.  */
  149.  
  150. XFontStruct *
  151. Tk_GetFontStruct(interp, tkwin, name)
  152.     Tcl_Interp *interp;        /* Place to leave error message if
  153.                  * font can't be found. */
  154.     Tk_Window tkwin;        /* Window in which font will be used. */
  155.     Tk_Uid name;        /* Name of font (in form suitable for
  156.                  * passing to XLoadQueryFont). */
  157. {
  158.     NameKey nameKey;
  159.     Tcl_HashEntry *nameHashPtr, *fontHashPtr;
  160.     int new;
  161.     register TkFont *fontPtr;
  162.     XFontStruct *fontStructPtr;
  163.  
  164.     if (!initialized) {
  165.     FontInit();
  166.     }
  167.  
  168.     /*
  169.      * First, check to see if there's already a mapping for this font
  170.      * name.
  171.      */
  172.  
  173.     nameKey.name = name;
  174.     nameKey.display = Tk_Display(tkwin);
  175.     nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new);
  176.     if (!new) {
  177.     fontPtr = (TkFont *) Tcl_GetHashValue(nameHashPtr);
  178.     fontPtr->refCount++;
  179.     return fontPtr->fontStructPtr;
  180.     }
  181.  
  182.     /*
  183.      * The name isn't currently known.  Map from the name to a font, and
  184.      * add a new structure to the database.
  185.      */
  186.  
  187.     fontStructPtr = XLoadQueryFont(nameKey.display, name);
  188.     if (fontStructPtr == NULL) {
  189.     Tcl_DeleteHashEntry(nameHashPtr);
  190.     Tcl_AppendResult(interp, "font \"", name, "\" doesn't exist",
  191.         (char *) NULL);
  192.     return NULL;
  193.     }
  194.     fontPtr = (TkFont *) ckalloc(sizeof(TkFont));
  195.     fontPtr->display = nameKey.display;
  196.     fontPtr->fontStructPtr = fontStructPtr;
  197.     fontPtr->refCount = 1;
  198.     fontPtr->types = NULL;
  199.     fontPtr->widths = NULL;
  200.     fontPtr->nameHashPtr = nameHashPtr;
  201.     fontHashPtr = Tcl_CreateHashEntry(&fontTable, (char *) fontStructPtr, &new);
  202.     if (!new) {
  203.     panic("XFontStruct already registered in Tk_GetFontStruct");
  204.     }
  205.     Tcl_SetHashValue(nameHashPtr, fontPtr);
  206.     Tcl_SetHashValue(fontHashPtr, fontPtr);
  207.     return fontPtr->fontStructPtr;
  208. }
  209.  
  210. /*
  211.  *--------------------------------------------------------------
  212.  *
  213.  * Tk_NameOfFontStruct --
  214.  *
  215.  *    Given a font, return a textual string identifying it.
  216.  *
  217.  * Results:
  218.  *    If font was created by Tk_GetFontStruct, then the return
  219.  *    value is the "string" that was used to create it.
  220.  *    Otherwise the return value is a string giving the X
  221.  *    identifier for the font.  The storage for the returned
  222.  *    string is only guaranteed to persist up until the next
  223.  *    call to this procedure.
  224.  *
  225.  * Side effects:
  226.  *    None.
  227.  *
  228.  *--------------------------------------------------------------
  229.  */
  230.  
  231. char *
  232. Tk_NameOfFontStruct(fontStructPtr)
  233.     XFontStruct *fontStructPtr;        /* Font whose name is desired. */
  234. {
  235.     Tcl_HashEntry *fontHashPtr;
  236.     TkFont *fontPtr;
  237.     static char string[20];
  238.  
  239.     if (!initialized) {
  240.     printid:
  241.     sprintf(string, "font id 0x%x", (unsigned int) fontStructPtr->fid);
  242.     return string;
  243.     }
  244.     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
  245.     if (fontHashPtr == NULL) {
  246.     goto printid;
  247.     }
  248.     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
  249.     return ((NameKey *) fontPtr->nameHashPtr->key.words)->name;
  250. }
  251.  
  252. /*
  253.  *----------------------------------------------------------------------
  254.  *
  255.  * Tk_FreeFontStruct --
  256.  *
  257.  *    This procedure is called to release a font allocated by
  258.  *    Tk_GetFontStruct.
  259.  *
  260.  * Results:
  261.  *    None.
  262.  *
  263.  * Side effects:
  264.  *    The reference count associated with font is decremented, and
  265.  *    the font is officially deallocated if no-one is using it
  266.  *    anymore.
  267.  *
  268.  *----------------------------------------------------------------------
  269.  */
  270.  
  271. void
  272. Tk_FreeFontStruct(fontStructPtr)
  273.     XFontStruct *fontStructPtr;    /* Font to be released. */
  274. {
  275.     Tcl_HashEntry *fontHashPtr;
  276.     register TkFont *fontPtr;
  277.  
  278.     if (!initialized) {
  279.     panic("Tk_FreeFontStruct called before Tk_GetFontStruct");
  280.     }
  281.  
  282.     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
  283.     if (fontHashPtr == NULL) {
  284.     panic("Tk_FreeFontStruct received unknown font argument");
  285.     }
  286.     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
  287.     fontPtr->refCount--;
  288.     if (fontPtr->refCount == 0) {
  289.     /*
  290.      * We really should call Tk_FreeXId below to release the font's
  291.      * resource identifier, but this seems to cause problems on
  292.      * many X servers (as of 5/1/94) where the font resource isn't
  293.      * really released, which can cause the wrong font to be used
  294.      * later on.  So, don't release the resource id after all, even
  295.      * though this results in an id leak.
  296.      *
  297.      * Tk_FreeXId(fontPtr->display, (XID) fontPtr->fontStructPtr->fid);
  298.      */
  299.  
  300.     XFreeFont(fontPtr->display, fontPtr->fontStructPtr);
  301.     Tcl_DeleteHashEntry(fontPtr->nameHashPtr);
  302.     Tcl_DeleteHashEntry(fontHashPtr);
  303.     if (fontPtr->types != NULL) {
  304.         ckfree(fontPtr->types);
  305.     }
  306.     if (fontPtr->widths != NULL) {
  307.         ckfree((char *) fontPtr->widths);
  308.     }
  309.     ckfree((char *) fontPtr);
  310.     lastFontStructPtr = NULL;
  311.     }
  312. }
  313.  
  314. /*
  315.  *----------------------------------------------------------------------
  316.  *
  317.  * FontInit --
  318.  *
  319.  *    Initialize the structure used for font management.
  320.  *
  321.  * Results:
  322.  *    None.
  323.  *
  324.  * Side effects:
  325.  *    Read the code.
  326.  *
  327.  *----------------------------------------------------------------------
  328.  */
  329.  
  330. static void
  331. FontInit()
  332. {
  333.     initialized = 1;
  334.     Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int));
  335.     Tcl_InitHashTable(&fontTable, TCL_ONE_WORD_KEYS);
  336. }
  337.  
  338. /*
  339.  *--------------------------------------------------------------
  340.  *
  341.  * SetFontMetrics --
  342.  *
  343.  *    This procedure is called to fill in the "widths" and "types"
  344.  *    arrays for a font.
  345.  *
  346.  * Results:
  347.  *    None.
  348.  *
  349.  * Side effects:
  350.  *    FontPtr gets modified to hold font metric information.
  351.  *
  352.  *--------------------------------------------------------------
  353.  */
  354.  
  355. static void
  356. SetFontMetrics(fontPtr)
  357.     register TkFont *fontPtr;        /* Font structure in which to
  358.                      * set metrics. */
  359. {
  360.     int i, replaceOK;
  361.     register XFontStruct *fontStructPtr = fontPtr->fontStructPtr;
  362.     char *p;
  363.  
  364.     /*
  365.      * Pass 1: initialize the arrays.
  366.      */
  367.  
  368.     fontPtr->types = (char *) ckalloc(256);
  369.     fontPtr->widths = (unsigned char *) ckalloc(256);
  370.     for (i = 0; i < 256; i++) {
  371.     fontPtr->types[i] = REPLACE;
  372.     }
  373.  
  374.     /*
  375.      * Pass 2:  for all characters that exist in the font and are
  376.      * not control characters, fill in the type and width
  377.      * information.
  378.      */
  379.  
  380.     for (i = 0; i < 256;  i++) {
  381.     if ((i == 0177) || (i < fontStructPtr->min_char_or_byte2)
  382.         || (i > fontStructPtr->max_char_or_byte2)) {
  383.         continue;
  384.     }
  385.     fontPtr->types[i] = NORMAL;
  386.     if (fontStructPtr->per_char == NULL) {
  387.         fontPtr->widths[i] = fontStructPtr->min_bounds.width;
  388.     } else {
  389.         fontPtr->widths[i] = fontStructPtr->per_char[i
  390.             - fontStructPtr->min_char_or_byte2].width;
  391.     }
  392.     }
  393.  
  394.     /*
  395.      * Pass 3: fill in information for characters that have to
  396.      * be replaced with  "\xhh" or "\n" strings.  If the font doesn't
  397.      * have the characters needed for this, then just use the
  398.      * font's default character.
  399.      */
  400.  
  401.     replaceOK = 1;
  402.     for (p = hexChars; *p != 0; p++) {
  403.     if (fontPtr->types[*p] != NORMAL) {
  404.         replaceOK = 0;
  405.         break;
  406.     }
  407.     }
  408.     for (i = 0; i < 256; i++) {
  409.     if (fontPtr->types[i] != REPLACE) {
  410.         continue;
  411.     }
  412.     if (replaceOK) {
  413.         if ((i < sizeof(mapChars)) && (mapChars[i] != 0)) {
  414.         fontPtr->widths[i] = fontPtr->widths['\\']
  415.             + fontPtr->widths[mapChars[i]];
  416.         } else {
  417.         fontPtr->widths[i] = fontPtr->widths['\\']
  418.             + fontPtr->widths['x']
  419.             + fontPtr->widths[hexChars[i & 0xf]]
  420.             + fontPtr->widths[hexChars[(i>>4) & 0xf]];
  421.         }
  422.     } else {
  423.         fontPtr->types[i] = SKIP;
  424.         fontPtr->widths[i] = 0;
  425.     }
  426.     }
  427.  
  428.     /*
  429.      * Lastly, fill in special information for newline and tab.
  430.      */
  431.  
  432.     fontPtr->types['\n'] = NEWLINE;
  433.     fontPtr->types['\t'] = TAB;
  434.     fontPtr->widths['\t'] = 0;
  435.     if (fontPtr->types['0'] == NORMAL) {
  436.     fontPtr->tabWidth = 8*fontPtr->widths['0'];
  437.     } else {
  438.     fontPtr->tabWidth = 8*fontStructPtr->max_bounds.width;
  439.     }
  440.  
  441.     /*
  442.      * Make sure the tab width isn't zero (some fonts may not have enough
  443.      * information to set a reasonable tab width).
  444.      */
  445.  
  446.     if (fontPtr->tabWidth == 0) {
  447.     fontPtr->tabWidth = 1;
  448.     }
  449. }
  450.  
  451. /*
  452.  *--------------------------------------------------------------
  453.  *
  454.  * TkMeasureChars --
  455.  *
  456.  *    Measure the number of characters from a string that
  457.  *    will fit in a given horizontal span.  The measurement
  458.  *    is done under the assumption that TkDisplayChars will
  459.  *    be used to actually display the characters.
  460.  *
  461.  * Results:
  462.  *    The return value is the number of characters from source
  463.  *    that fit in the span given by startX and maxX.  *nextXPtr
  464.  *    is filled in with the x-coordinate at which the first
  465.  *    character that didn't fit would be drawn, if it were to
  466.  *    be drawn.
  467.  *
  468.  * Side effects:
  469.  *    None.
  470.  *
  471.  *--------------------------------------------------------------
  472.  */
  473.  
  474. int
  475. TkMeasureChars(fontStructPtr, source, maxChars, startX, maxX,
  476.     tabOrigin, flags, nextXPtr)
  477.     XFontStruct *fontStructPtr;    /* Font in which to draw characters. */
  478.     char *source;        /* Characters to be displayed.  Need not
  479.                  * be NULL-terminated. */
  480.     int maxChars;        /* Maximum # of characters to consider from
  481.                  * source. */
  482.     int startX;            /* X-position at which first character will
  483.                  * be drawn. */
  484.     int maxX;            /* Don't consider any character that would
  485.                  * cross this x-position. */
  486.     int tabOrigin;        /* X-location that serves as "origin" for
  487.                  * tab stops. */
  488.     int flags;            /* Various flag bits OR-ed together.
  489.                  * TK_WHOLE_WORDS means stop on a word boundary
  490.                  * (just before a space character) if
  491.                  * possible.  TK_AT_LEAST_ONE means always
  492.                  * return a value of at least one, even
  493.                  * if the character doesn't fit. 
  494.                  * TK_PARTIAL_OK means it's OK to display only
  495.                  * a part of the last character in the line.
  496.                  * TK_NEWLINES_NOT_SPECIAL means that newlines
  497.                  * are treated just like other control chars:
  498.                  * they don't terminate the line.
  499.                  * TK_IGNORE_TABS means give all tabs zero
  500.                  * width. */
  501.     int *nextXPtr;        /* Return x-position of terminating
  502.                  * character here. */
  503. {
  504.     register TkFont *fontPtr;
  505.     register char *p;        /* Current character. */
  506.     register int c;
  507.     char *term;            /* Pointer to most recent character that
  508.                  * may legally be a terminating character. */
  509.     int termX;            /* X-position just after term. */
  510.     int curX;            /* X-position corresponding to p. */
  511.     int newX;            /* X-position corresponding to p+1. */
  512.     int type;
  513.     int rem;
  514.  
  515.     /*
  516.      * Find the TkFont structure for this font, and make sure its
  517.      * font metrics exist.
  518.      */
  519.  
  520.     if (lastFontStructPtr == fontStructPtr) {
  521.     fontPtr = lastFontPtr;
  522.     } else {
  523.     Tcl_HashEntry *fontHashPtr;
  524.  
  525.     if (!initialized) {
  526.         badArg:
  527.         panic("TkMeasureChars received unknown font argument");
  528.     }
  529.     
  530.     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
  531.     if (fontHashPtr == NULL) {
  532.         goto badArg;
  533.     }
  534.     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
  535.     lastFontStructPtr = fontPtr->fontStructPtr;
  536.     lastFontPtr = fontPtr;
  537.     }
  538.     if (fontPtr->types == NULL) {
  539.     SetFontMetrics(fontPtr);
  540.     }
  541.  
  542.     /*
  543.      * Scan the input string one character at a time, until a character
  544.      * is found that crosses maxX.
  545.      */
  546.  
  547.     newX = curX = startX;
  548.     termX = 0;        /* Not needed, but eliminates compiler warning. */
  549.     term = source;
  550.     for (p = source, c = *p & 0xff; maxChars > 0; p++, maxChars--) {
  551.     type = fontPtr->types[c];
  552.     if ((type == NORMAL) || (type == REPLACE)) {
  553.         newX += fontPtr->widths[c];
  554.     } else if (type == TAB) {
  555.         if (!(flags & TK_IGNORE_TABS)) {
  556.         newX += fontPtr->tabWidth;
  557.         rem = (newX - tabOrigin) % fontPtr->tabWidth;
  558.         if (rem < 0) {
  559.             rem += fontPtr->tabWidth;
  560.         }
  561.         newX -= rem;
  562.         }
  563.     } else if (type == NEWLINE) {
  564.         if (flags & TK_NEWLINES_NOT_SPECIAL) {
  565.         newX += fontPtr->widths[c];
  566.         } else {
  567.         break;
  568.         }
  569.     } else if (type != SKIP) {
  570.         panic("Unknown type %d in TkMeasureChars", type);
  571.     }
  572.     if (newX > maxX) {
  573.         break;
  574.     }
  575.     c = p[1] & 0xff;
  576.     if (isspace(UCHAR(c)) || (c == 0)) {
  577.         term = p+1;
  578.         termX = newX;
  579.     }
  580.     curX = newX;
  581.     }
  582.  
  583.     /*
  584.      * P points to the first character that doesn't fit in the desired
  585.      * span.  Use the flags to figure out what to return.
  586.      */
  587.  
  588.     if ((flags & TK_PARTIAL_OK) && (curX < maxX)) {
  589.     curX = newX;
  590.     p++;
  591.     }
  592.     if ((flags & TK_AT_LEAST_ONE) && (term == source) && (maxChars > 0)
  593.          && !isspace(UCHAR(*term))) {
  594.     term = p;
  595.     termX = curX;
  596.     if (term == source) {
  597.         term++;
  598.         termX = newX;
  599.     }
  600.     } else if ((maxChars == 0) || !(flags & TK_WHOLE_WORDS)) {
  601.     term = p;
  602.     termX = curX;
  603.     }
  604.     *nextXPtr = termX;
  605.     return term-source;
  606. }
  607.  
  608. /*
  609.  *--------------------------------------------------------------
  610.  *
  611.  * TkDisplayChars --
  612.  *
  613.  *    Draw a string of characters on the screen, converting
  614.  *    tabs to the right number of spaces and control characters
  615.  *    to sequences of the form "\xhh" where hh are two hex
  616.  *    digits.
  617.  *
  618.  * Results:
  619.  *    None.
  620.  *
  621.  * Side effects:
  622.  *    Information gets drawn on the screen.
  623.  *
  624.  *--------------------------------------------------------------
  625.  */
  626.  
  627. void
  628. TkDisplayChars(display, drawable, gc, fontStructPtr, string, numChars,
  629.     x, y, tabOrigin, flags)
  630.     Display *display;        /* Display on which to draw. */
  631.     Drawable drawable;        /* Window or pixmap in which to draw. */
  632.     GC gc;            /* Graphics context for actually drawing
  633.                  * characters. */
  634.     XFontStruct *fontStructPtr;    /* Font used in GC;  must have been allocated
  635.                  * by Tk_GetFontStruct.  Used to compute sizes
  636.                  * of tabs, etc. */
  637.     char *string;        /* Characters to be displayed. */
  638.     int numChars;        /* Number of characters to display from
  639.                  * string. */
  640.     int x, y;            /* Coordinates at which to draw string. */
  641.     int tabOrigin;        /* X-location that serves as "origin" for
  642.                  * tab stops. */
  643.     int flags;            /* Flags to control display.  Only
  644.                  * TK_NEWLINES_NOT_SPECIAL and TK_IGNORE_TABS
  645.                  * are supported right now.  See
  646.                  * TkMeasureChars for information about it. */
  647. {
  648.     register TkFont *fontPtr;
  649.     register char *p;        /* Current character being scanned. */
  650.     register int c;
  651.     int type;
  652.     char *start;        /* First character waiting to be displayed. */
  653.     int startX;            /* X-coordinate corresponding to start. */
  654.     int curX;            /* X-coordinate corresponding to p. */
  655.     char replace[10];
  656.     int rem;
  657.  
  658.     /*
  659.      * Find the TkFont structure for this font, and make sure its
  660.      * font metrics exist.
  661.      */
  662.  
  663.     if (lastFontStructPtr == fontStructPtr) {
  664.     fontPtr = lastFontPtr;
  665.     } else {
  666.     Tcl_HashEntry *fontHashPtr;
  667.  
  668.     if (!initialized) {
  669.         badArg:
  670.         panic("TkDisplayChars received unknown font argument");
  671.     }
  672.     
  673.     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
  674.     if (fontHashPtr == NULL) {
  675.         goto badArg;
  676.     }
  677.     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
  678.     lastFontStructPtr = fontPtr->fontStructPtr;
  679.     lastFontPtr = fontPtr;
  680.     }
  681.     if (fontPtr->types == NULL) {
  682.     SetFontMetrics(fontPtr);
  683.     }
  684.  
  685.     /*
  686.      * Scan the string one character at a time.  Display control
  687.      * characters immediately, but delay displaying normal characters
  688.      * in order to pass many characters to the server all together.
  689.      */
  690.  
  691.     startX = curX = x;
  692.     start = string;
  693.     for (p = string; numChars > 0; numChars--, p++) {
  694.     c = *p & 0xff;
  695.     type = fontPtr->types[c];
  696.     if (type == NORMAL) {
  697.         curX += fontPtr->widths[c];
  698.         continue;
  699.     }
  700.     if (p != start) {
  701.         XDrawString(display, drawable, gc, startX, y, start, p - start);
  702.         startX = curX;
  703.     }
  704.     if (type == TAB) {
  705.         if (!(flags & TK_IGNORE_TABS)) {
  706.         curX += fontPtr->tabWidth;
  707.         rem = (curX - tabOrigin) % fontPtr->tabWidth;
  708.         if (rem < 0) {
  709.             rem += fontPtr->tabWidth;
  710.         }
  711.         curX -= rem;
  712.         }
  713.     } else if (type == REPLACE || 
  714.         (type == NEWLINE && flags & TK_NEWLINES_NOT_SPECIAL)) {
  715.         if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) {
  716.         replace[0] = '\\';
  717.             replace[1] = mapChars[c];
  718.             XDrawString(display, drawable, gc, startX, y, replace, 2);
  719.             curX += fontPtr->widths[replace[0]]
  720.                 + fontPtr->widths[replace[1]];
  721.         } else {
  722.         replace[0] = '\\';
  723.             replace[1] = 'x';
  724.             replace[2] = hexChars[(c >> 4) & 0xf];
  725.             replace[3] = hexChars[c & 0xf];
  726.             XDrawString(display, drawable, gc, startX, y, replace, 4);
  727.             curX += fontPtr->widths[replace[0]]
  728.                 + fontPtr->widths[replace[1]]
  729.                 + fontPtr->widths[replace[2]]
  730.                 + fontPtr->widths[replace[3]];
  731.           }
  732.     } else if (type == NEWLINE) {
  733.           y += fontStructPtr->ascent + fontStructPtr->descent;
  734.         curX = x;
  735.     } else if (type != SKIP) {
  736.         panic("Unknown type %d in TkDisplayChars", type);
  737.     }
  738.     startX = curX;
  739.     start = p+1;
  740.     }
  741.  
  742.     /*
  743.      * At the very end, there may be one last batch of normal characters
  744.      * to display.
  745.      */
  746.  
  747.     if (p != start) {
  748.     XDrawString(display, drawable, gc, startX, y, start, p - start);
  749.     }
  750. }
  751.  
  752. /*
  753.  *----------------------------------------------------------------------
  754.  *
  755.  * TkUnderlineChars --
  756.  *
  757.  *    This procedure draws an underline for a given range of characters
  758.  *    in a given string, using appropriate information for the string's
  759.  *    font.  It doesn't draw the characters (which are assumed to have
  760.  *    been displayed previously);  it just draws the underline.
  761.  *
  762.  * Results:
  763.  *    None.
  764.  *
  765.  * Side effects:
  766.  *    Information gets displayed in "drawable".
  767.  *
  768.  *----------------------------------------------------------------------
  769.  */
  770.  
  771. void
  772. TkUnderlineChars(display, drawable, gc, fontStructPtr, string, x, y,
  773.     tabOrigin, flags, firstChar, lastChar)
  774.     Display *display;        /* Display on which to draw. */
  775.     Drawable drawable;        /* Window or pixmap in which to draw. */
  776.     GC gc;            /* Graphics context for actually drawing
  777.                  * underline. */
  778.     XFontStruct *fontStructPtr;    /* Font used in GC;  must have been allocated
  779.                  * by Tk_GetFontStruct.  Used to character
  780.                  * dimensions, etc. */
  781.     char *string;        /* String containing characters to be
  782.                  * underlined. */
  783.     int x, y;            /* Coordinates at which first character of
  784.                  * string is drawn. */
  785.     int tabOrigin;        /* X-location that serves as "origin" for
  786.                  * tab stops. */
  787.     int flags;            /* Flags that were passed to TkDisplayChars. */
  788.     int firstChar;        /* Index of first character to underline. */
  789.     int lastChar;        /* Index of last character to underline. */
  790. {
  791.     int xUnder, yUnder, width, height;
  792.     unsigned long value;
  793.  
  794.     /*
  795.      * First compute the vertical span of the underline, using font
  796.      * properties if they exist.
  797.      */
  798.  
  799.     if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_POSITION, &value)) {
  800.     yUnder = y + value;
  801.     } else {
  802.     yUnder = y + fontStructPtr->max_bounds.descent/2;
  803.     }
  804.     if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_THICKNESS, &value)) {
  805.     height = value;
  806.     } else {
  807.     height = 2;
  808.     }
  809.  
  810.     /*
  811.      * Now compute the horizontal span of the underline.
  812.      */
  813.  
  814.     TkMeasureChars(fontStructPtr, string, firstChar, x, (int) 1000000,
  815.         tabOrigin, flags, &xUnder);
  816.     TkMeasureChars(fontStructPtr, string+firstChar, lastChar+1-firstChar,
  817.         xUnder, (int) 1000000, tabOrigin, flags, &width);
  818.     width -= xUnder;
  819.  
  820.     XFillRectangle(display, drawable, gc, xUnder, yUnder,
  821.         (unsigned int) width, (unsigned int) height);
  822. }
  823.  
  824. /*
  825.  *----------------------------------------------------------------------
  826.  *
  827.  * TkComputeTextGeometry --
  828.  *
  829.  *    This procedure computes the amount of screen space needed to
  830.  *    display a multi-line string of text.
  831.  *
  832.  * Results:
  833.  *    There is no return value.  The dimensions of the screen area
  834.  *    needed to display the text are returned in *widthPtr, and *heightPtr.
  835.  *
  836.  * Side effects:
  837.  *    None.
  838.  *
  839.  *----------------------------------------------------------------------
  840.  */
  841.  
  842. void
  843. TkComputeTextGeometry(fontStructPtr, string, numChars, wrapLength,
  844.     widthPtr, heightPtr)
  845.     XFontStruct *fontStructPtr;    /* Font that will be used to display text. */
  846.     char *string;        /* String whose dimensions are to be
  847.                  * computed. */
  848.     int numChars;        /* Number of characters to consider from
  849.                  * string. */
  850.     int wrapLength;        /* Longest permissible line length, in
  851.                  * pixels.  <= 0 means no automatic wrapping:
  852.                  * just let lines get as long as needed. */
  853.     int *widthPtr;        /* Store width of string here. */
  854.     int *heightPtr;        /* Store height of string here. */
  855. {
  856.     int thisWidth, maxWidth, numLines;
  857.     char *p;
  858.  
  859.     if (wrapLength <= 0) {
  860.     wrapLength = INT_MAX;
  861.     }
  862.     maxWidth = 0;
  863.     for (numLines = 1, p = string; (p - string) < numChars; numLines++) {
  864.     p += TkMeasureChars(fontStructPtr, p, numChars - (p - string), 0,
  865.         wrapLength, 0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &thisWidth);
  866.     if (thisWidth > maxWidth) {
  867.         maxWidth = thisWidth;
  868.     }
  869.     if (*p == 0) {
  870.         break;
  871.     }
  872.  
  873.     /*
  874.      * If the character that didn't fit in this line was a white
  875.      * space character then skip it.
  876.      */
  877.  
  878.     if (isspace(UCHAR(*p))) {
  879.         p++;
  880.     }
  881.     }
  882.     *widthPtr = maxWidth;
  883.     *heightPtr = numLines * (fontStructPtr->ascent + fontStructPtr->descent);
  884. }
  885.  
  886. /*
  887.  *----------------------------------------------------------------------
  888.  *
  889.  * TkDisplayText --
  890.  *
  891.  *    Display a text string on one or more lines.
  892.  *
  893.  * Results:
  894.  *    None.
  895.  *
  896.  * Side effects:
  897.  *    The text given by "string" gets displayed at the given location
  898.  *    in the given drawable with the given font etc.
  899.  *
  900.  *----------------------------------------------------------------------
  901.  */
  902.  
  903. void
  904. TkDisplayText(display, drawable, fontStructPtr, string, numChars, x, y,
  905.     length, justify, underline, gc)
  906.     Display *display;        /* X display to use for drawing text. */
  907.     Drawable drawable;        /* Window or pixmap in which to draw the
  908.                  * text. */
  909.     XFontStruct *fontStructPtr;    /* Font that determines geometry of text
  910.                  * (should be same as font in gc). */
  911.     char *string;        /* String to display;  may contain embedded
  912.                  * newlines. */
  913.     int numChars;        /* Number of characters to use from string. */
  914.     int x, y;            /* Pixel coordinates within drawable of
  915.                  * upper left corner of display area. */
  916.     int length;            /* Line length in pixels;  used to compute
  917.                  * word wrap points and also for
  918.                  * justification.   Must be > 0. */
  919.     Tk_Justify justify;        /* How to justify lines. */
  920.     int underline;        /* Index of character to underline, or < 0
  921.                  * for no underlining. */
  922.     GC gc;            /* Graphics context to use for drawing text. */
  923. {
  924.     char *p;
  925.     int charsThisLine, lengthThisLine, xThisLine;
  926.  
  927.     /*
  928.      * Work through the string one line at a time.  Display each line
  929.      * in four steps:
  930.      *     1. Compute the line's length.
  931.      *     2. Figure out where to display the line for justification.
  932.      *     3. Display the line.
  933.      *     4. Underline one character if needed.
  934.      */
  935.  
  936.     y += fontStructPtr->ascent;
  937.     for (p = string; numChars > 0; ) {
  938.     charsThisLine = TkMeasureChars(fontStructPtr, p, numChars, 0, length,
  939.         0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &lengthThisLine);
  940.     if (justify == TK_JUSTIFY_LEFT) {
  941.         xThisLine = x;
  942.     } else if (justify == TK_JUSTIFY_CENTER) {
  943.         xThisLine = x + (length - lengthThisLine)/2;
  944.     } else {
  945.         xThisLine = x + (length - lengthThisLine);
  946.     }
  947.     TkDisplayChars(display, drawable, gc, fontStructPtr, p, charsThisLine,
  948.         xThisLine, y, xThisLine, 0);
  949.     if ((underline >= 0) && (underline < charsThisLine)) {
  950.         TkUnderlineChars(display, drawable, gc, fontStructPtr, p,
  951.             xThisLine, y, xThisLine, 0, underline, underline);
  952.     }
  953.     p += charsThisLine;
  954.     numChars -= charsThisLine;
  955.     underline -= charsThisLine;
  956.     y += fontStructPtr->ascent + fontStructPtr->descent;
  957.  
  958.     /*
  959.      * If the character that didn't fit was a space character, skip it.
  960.      */
  961.  
  962.     if (isspace(UCHAR(*p))) {
  963.         p++;
  964.         numChars--;
  965.         underline--;
  966.     }
  967.     }
  968. }
  969.